package model;

import java.util.Observable;

/**
 * Board class represents a Minesweeper's game board: it is built as a matrix of
 * {@link Box} istances and supports the whole set of Minesweeper's game
 * actions, that is box's selection and box's marking. If a box is selected
 * while hiding a mine, that will result in a {@link MineException}
 * 
 * @author simone
 *
 */
public class Board extends Observable {

	private Box[][] board;
	private int width;
	private int height;

	/**
	 * Returns the board incapsulated by this class
	 * 
	 * @return: a matrix of {@link Box} istances representing the board
	 */
	public Box[][] getBoard() {
		return board;
	}

	/**
	 * Construct a new istance of this class, using the {@link IGenerator}
	 * istance as builder
	 * 
	 * @param generator
	 *            : the concrete implementation of {@link IGenerator} that will
	 *            be used to create this board
	 */
	public Board(IGenerator generator) {
		this.board = generator.generate();
		this.width = generator.getBoardWidth();
		this.height = generator.getBoardHeight();
	}

	public int getWidth() {
		return width;
	}

	public int getHeight() {
		return height;
	}

	/**
	 * Select this box, wich means being sure that this box isn't hiding a mine
	 * 
	 * @param xBox
	 *            : index (x axis) of the box
	 * @param yBox
	 *            : index (y axis) of the box
	 * @return: an integer representing the number of mines adjacent to this box
	 * @throws MineException
	 *             : if this box was hiding a mine
	 */
	public int selectBox(int xBox, int yBox) throws MineException  {
		board[xBox][yBox].select();
		setChanged();
		notifyObservers();
		return adjacentMines(xBox, yBox);

	}

	/**
	 * Mark this box as a mine-hiding one. You cannot mark a selected box.
	 * Invoked on a marked box, this method will unmark the box itself
	 * 
	 * @param xBox
	 *            : index (x axis) of this box
	 * @param yBox
	 *            : index (y axis) of this box
	 */
	public void markBox(int xBox, int yBox) {
		try {
			this.board[xBox][yBox].mark();
			setChanged();
			notifyObservers();
		} catch (Exception e) {
		}
	}

	private int adjacentMines(int xBox, int yBox) {
		int adjacentMines = 0;
		for (int i = xBox - 1; i <= xBox + 1; i++) {
			for (int j = yBox - 1; j <= yBox + 1; j++) {
				Box box;
				try {
					box = board[i][j];
					if (box.hasMine()) {
						adjacentMines++;
					}
				} catch (Exception e) {
				}
			}
		}
		return adjacentMines;
	}

}
